/*-------------------------------------------------------------------------
 *
 * parse_oper.c
 *		handle operator things for parser
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_oper.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/builtins.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"


/*
 * 操作符 lookaside 哈希表的查找键。未使用的位必须为
 * 零，以确保哈希一致性工作——特别是，oprname
 * 必须填充零，search_path 中的任何未使用条目也必须为零。
 *
 * search_path 包含实际上与条目派生的搜索路径
 * （如果有，减去临时命名空间），或者，如果我们正在查找
 * 明确限定的操作符名称，则为单个指定的
 * 模式 OID。
 *
 * search_path 必须是固定长度，因为哈希表代码坚持于
 * 固定大小的键。如果您的搜索路径超出此长度，我们将放弃
 * 并且不缓存任何内容。
 */

/* 如果你的 search_path 比这个长，那就不好说了... */
#define MAX_CACHED_PATH_LEN		16

typedef struct OprCacheKey
{
	char		oprname[NAMEDATALEN];
	Oid			left_arg;		/* 左输入 OID，或者如果是前缀操作符则为 0 */
	Oid			right_arg;		/* 右输入 OID */
	Oid			search_path[MAX_CACHED_PATH_LEN];
} OprCacheKey;

typedef struct OprCacheEntry
{
	/* 哈希查找键必须是第一个 */
	OprCacheKey key;

	Oid			opr_oid;		/* 已解析操作符的 OID */
} OprCacheEntry;


static Oid	fc_binary_oper_exact(List *fc_opname, Oid fc_arg1, Oid fc_arg2);
static FuncDetailCode fc_oper_select_candidate(int fc_nargs,
											Oid *fc_input_typeids,
											FuncCandidateList fc_candidates,
											Oid *fc_operOid);
static const char *fc_op_signature_string(List *fc_op, char fc_oprkind,
									   Oid fc_arg1, Oid fc_arg2);
static void fc_op_error(ParseState *fc_pstate, List *fc_op, char fc_oprkind,
					 Oid fc_arg1, Oid fc_arg2,
					 FuncDetailCode fc_fdresult, int fc_location);
static bool fc_make_oper_cache_key(ParseState *fc_pstate, OprCacheKey *fc_key,
								List *fc_opname, Oid fc_ltypeId, Oid fc_rtypeId,
								int fc_location);
static Oid	fc_find_oper_cache_entry(OprCacheKey *fc_key);
static void fc_make_oper_cache_entry(OprCacheKey *fc_key, Oid fc_opr_oid);
static void fc_InvalidateOprCacheCallBack(Datum fc_arg, int fc_cacheid, uint32 fc_hashvalue);


/*
 * LookupOperName
 *	 给定一个可能合格的操作符名称和确切的输入数据类型，
 *	 查找操作符。
 *
 * 将 oprleft 传递为 InvalidOid 以表示前缀操作符。
 *
 * 如果操作符名称没有模式限定，它将在当前
 * 命名空间搜索路径中寻找。
 *
 * 如果找不到操作符，如果 noError 为 true，则返回 InvalidOid，
 * 否则引发错误。 pstate 和 location 仅用于报告
 * 错误位置；如果不可用，请传递 NULL/-1。
 */
Oid LookupOperName(ParseState *fc_pstate, List *fc_opername, Oid fc_oprleft, Oid fc_oprright,
			   bool fc_noError, int fc_location)
{
	Oid			fc_result;

	fc_result = OpernameGetOprid(fc_opername, fc_oprleft, fc_oprright);
	if (OidIsValid(fc_result))
		return fc_result;

	/* 我们在这里不使用 op_error，因为只需要一个精确匹配 */
	if (!fc_noError)
	{
		char		fc_oprkind;

		if (!OidIsValid(fc_oprleft))
			fc_oprkind = 'l';
		else if (OidIsValid(fc_oprright))
			fc_oprkind = 'b';
		else
		{
			ereport(ERROR,
					(errcode(ERRCODE_SYNTAX_ERROR),
					 errmsg("postfix operators are not supported"),
					 parser_errposition(fc_pstate, fc_location)));
			fc_oprkind = 0;		/* 保持编译器安静 */
		}

		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator does not exist: %s",
						fc_op_signature_string(fc_opername, fc_oprkind,
											fc_oprleft, fc_oprright)),
				 parser_errposition(fc_pstate, fc_location)));
	}

	return InvalidOid;
}

/*
 * LookupOperWithArgs
 *	 类似于 LookupOperName，但参数类型由
 *	 ObjectWithArgs 节点指定。
 */
Oid LookupOperWithArgs(ObjectWithArgs *fc_oper, bool fc_noError)
{
	TypeName   *fc_oprleft,
			   *fc_oprright;
	Oid			fc_leftoid,
				fc_rightoid;

	Assert(list_length(fc_oper->objargs) == 2);
	fc_oprleft = linitial_node(TypeName, fc_oper->objargs);
	fc_oprright = lsecond_node(TypeName, fc_oper->objargs);

	if (fc_oprleft == NULL)
		fc_leftoid = InvalidOid;
	else
		fc_leftoid = LookupTypeNameOid(NULL, fc_oprleft, fc_noError);

	if (fc_oprright == NULL)
		fc_rightoid = InvalidOid;
	else
		fc_rightoid = LookupTypeNameOid(NULL, fc_oprright, fc_noError);

	return LookupOperName(NULL, fc_oper->objname, fc_leftoid, fc_rightoid,
						  fc_noError, -1);
}

/*
 * get_sort_group_operators - 获取类型的默认排序/分组操作符
 *
 * 我们一次获取 "<"、"=" 和 ">" 操作符，以减少查找
 * 开销（因为大多数调用者至少对两者感兴趣）。
 * 然而，给定的数据类型可能只有 "=" 操作符，如果它是
 * 可散列但不可排序的。 （其他存在和缺少的操作符组合不应该发生，
 * 除非系统目录出现问题。）
 *
 * 如果缺少操作符并且对应的 needXX 标志为 true，
 * 则抛出标准错误消息，否则返回 InvalidOid。
 *
 * 除了操作符 OID 本身之外，该函数可以识别
 * "=" 操作符是否可散列。
 *
 * 调用者可以传递 NULL 指针以获取他们不关心的任何结果。
 *
 * 注意：结果保证是精确或二进制兼容的匹配，
 * 因为大多数调用者不准备处理任何运行时类型
 * 强制转换步骤。
 */
void get_sort_group_operators(Oid fc_argtype,
						 bool fc_needLT, bool fc_needEQ, bool fc_needGT,
						 Oid *fc_ltOpr, Oid *fc_eqOpr, Oid *fc_gtOpr,
						 bool *fc_isHashable)
{
	TypeCacheEntry *fc_typentry;
	int			fc_cache_flags;
	Oid			fc_lt_opr;
	Oid			fc_eq_opr;
	Oid			fc_gt_opr;
	bool		fc_hashable;

	/*
	 * 使用类型缓存查找操作符。
	 *
	 * 注意：typcache.c 使用的搜索算法确保结果
	 * 是一致的，即均来自匹配的 opclass。
	 */
	if (fc_isHashable != NULL)
		fc_cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR |
			TYPECACHE_HASH_PROC;
	else
		fc_cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR;

	fc_typentry = lookup_type_cache(fc_argtype, fc_cache_flags);
	fc_lt_opr = fc_typentry->lt_opr;
	fc_eq_opr = fc_typentry->eq_opr;
	fc_gt_opr = fc_typentry->gt_opr;
	fc_hashable = OidIsValid(fc_typentry->hash_proc);

	/* 如果需要，则报告错误 */
	if ((fc_needLT && !OidIsValid(fc_lt_opr)) ||
		(fc_needGT && !OidIsValid(fc_gt_opr)))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("could not identify an ordering operator for type %s",
						format_type_be(fc_argtype)),
				 errhint("Use an explicit ordering operator or modify the query.")));
	if (fc_needEQ && !OidIsValid(fc_eq_opr))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("could not identify an equality operator for type %s",
						format_type_be(fc_argtype))));

	/* 根据需要返回结果 */
	if (fc_ltOpr)
		*fc_ltOpr = fc_lt_opr;
	if (fc_eqOpr)
		*fc_eqOpr = fc_eq_opr;
	if (fc_gtOpr)
		*fc_gtOpr = fc_gt_opr;
	if (fc_isHashable)
		*fc_isHashable = fc_hashable;
}


/* 给定操作符元组，返回操作符 OID */
Oid oprid(Operator fc_op)
{
	return ((Form_pg_operator) GETSTRUCT(fc_op))->oid;
}

/* 给定操作符元组，返回底层函数的 OID */
Oid oprfuncid(Operator fc_op)
{
	Form_pg_operator fc_pgopform = (Form_pg_operator) GETSTRUCT(fc_op);

	return fc_pgopform->oprcode;
}


/* binary_oper_exact()
 * 检查指定操作数类型的 "精确" 匹配。
 *
 * 如果一个操作数是未知字面量，假设它应该与
 * 另一个操作数具有相同的类型。此外，还要考虑
 * 另一个操作数是需要
 * 降级到其基本类型以找到 "精确" 匹配的域类型的可能性。
 */
static Oid fc_binary_oper_exact(List *fc_opname, Oid fc_arg1, Oid fc_arg2)
{
	Oid			fc_result;
	bool		fc_was_unknown = false;

	/* 其中一个参数类型未指定？那就使用另一个 */
	if ((fc_arg1 == UNKNOWNOID) && (fc_arg2 != InvalidOid))
	{
		fc_arg1 = fc_arg2;
		fc_was_unknown = true;
	}
	else if ((fc_arg2 == UNKNOWNOID) && (fc_arg1 != InvalidOid))
	{
		fc_arg2 = fc_arg1;
		fc_was_unknown = true;
	}

	fc_result = OpernameGetOprid(fc_opname, fc_arg1, fc_arg2);
	if (OidIsValid(fc_result))
		return fc_result;

	if (fc_was_unknown)
	{
		/* arg1 和 arg2 在这里是相同的，只需查看 arg1 */
		Oid			fc_basetype = getBaseType(fc_arg1);

		if (fc_basetype != fc_arg1)
		{
			fc_result = OpernameGetOprid(fc_opname, fc_basetype, fc_basetype);
			if (OidIsValid(fc_result))
				return fc_result;
		}
	}

	return InvalidOid;
}


/* oper_select_candidate()
 *	 给定输入 argtype 数组和一个或多个候选
 *	 操作符，尝试解决冲突。
 *
 * 返回 FUNCDETAIL_NOTFOUND、FUNCDETAIL_MULTIPLE 或 FUNCDETAIL_NORMAL。
 * 在成功的情况下，最佳候选的 Oid 存储在 *operOid 中。
 *
 * 注意，调用者已经确定没有完全匹配输入 argtype 的候选。
 * 不兼容的候选尚未剔除。
 */
static FuncDetailCode
fc_oper_select_candidate(int fc_nargs,
					  Oid *fc_input_typeids,
					  FuncCandidateList fc_candidates,
					  Oid *fc_operOid) /* 输出参数 */
{
	int			fc_ncandidates;

	/*
	 * 删除任何实际上无法接受给定输入
	 * 类型的候选者，无论是直接还是通过强制转换。
	 */
	fc_ncandidates = func_match_argtypes(fc_nargs, fc_input_typeids,
									  fc_candidates, &fc_candidates);

	/* 如果没有候选者或只有一个候选者存活，则完成 */
	if (fc_ncandidates == 0)
	{
		*fc_operOid = InvalidOid;
		return FUNCDETAIL_NOTFOUND;
	}
	if (fc_ncandidates == 1)
	{
		*fc_operOid = fc_candidates->oid;
		return FUNCDETAIL_NORMAL;
	}

	/*
	 * 使用与模糊函数相同的启发式来解决
	 * 冲突。
	 */
	fc_candidates = func_select_candidate(fc_nargs, fc_input_typeids, fc_candidates);

	if (fc_candidates)
	{
		*fc_operOid = fc_candidates->oid;
		return FUNCDETAIL_NORMAL;
	}

	*fc_operOid = InvalidOid;
	return FUNCDETAIL_MULTIPLE; /* 未能选择最佳候选人 */
}


/* oper() -- 搜索二元操作符
 * 给定操作符名称、arg1 和 arg2 的类型，返回 oper 结构。
 *
 * 重要提示：返回的操作符（如果有）仅承诺与
 * 输入数据类型兼容。 如果您需要精确或二进制兼容的匹配，请参见 compatible_oper。
 *
 * 如果未找到匹配的操作符，如果 noError 为 true，则返回 NULL，
 * 如果为 false，则引发错误。 pstate 和 location 仅用于报告
 * 错误位置；如果不可用，请传递 NULL/-1。
 *
 * 注意：在成功时，返回的对象是 syscache 条目。当完成后，调用者
 * 必须 ReleaseSysCache() 该条目。
 */
Operator oper(ParseState *fc_pstate, List *fc_opname, Oid fc_ltypeId, Oid fc_rtypeId,
	 bool fc_noError, int fc_location)
{
	Oid			fc_operOid;
	OprCacheKey fc_key;
	bool		fc_key_ok;
	FuncDetailCode fc_fdresult = FUNCDETAIL_NOTFOUND;
	HeapTuple	fc_tup = NULL;

	/*
	 * 尝试在旁路缓存中找到映射。
	 */
	fc_key_ok = fc_make_oper_cache_key(fc_pstate, &fc_key, fc_opname, fc_ltypeId, fc_rtypeId, fc_location);

	if (fc_key_ok)
	{
		fc_operOid = fc_find_oper_cache_entry(&fc_key);
		if (OidIsValid(fc_operOid))
		{
			fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));
			if (HeapTupleIsValid(fc_tup))
				return (Operator) fc_tup;
		}
	}

	/*
	 * 首先尝试 "精确" 匹配。
	 */
	fc_operOid = fc_binary_oper_exact(fc_opname, fc_ltypeId, fc_rtypeId);
	if (!OidIsValid(fc_operOid))
	{
		/*
		 * 否则，搜索最合适的候选者。
		 */
		FuncCandidateList fc_clist;

		/* 获取给定名称的二元操作符 */
		fc_clist = OpernameGetCandidates(fc_opname, 'b', false);

		/* 找不到操作符？那就失败... */
		if (fc_clist != NULL)
		{
			/*
			 * 参数之一的类型未指定？那么使用另一个
			 * （XXX 这可能是死代码？）
			 */
			Oid			fc_inputOids[2];

			if (fc_rtypeId == InvalidOid)
				fc_rtypeId = fc_ltypeId;
			else if (fc_ltypeId == InvalidOid)
				fc_ltypeId = fc_rtypeId;
			fc_inputOids[0] = fc_ltypeId;
			fc_inputOids[1] = fc_rtypeId;
			fc_fdresult = fc_oper_select_candidate(2, fc_inputOids, fc_clist, &fc_operOid);
		}
	}

	if (OidIsValid(fc_operOid))
		fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));

	if (HeapTupleIsValid(fc_tup))
	{
		if (fc_key_ok)
			fc_make_oper_cache_entry(&fc_key, fc_operOid);
	}
	else if (!fc_noError)
		fc_op_error(fc_pstate, fc_opname, 'b', fc_ltypeId, fc_rtypeId, fc_fdresult, fc_location);

	return (Operator) fc_tup;
}

/* compatible_oper()
 *	给定操作名和输入数据类型，找到一个兼容的二元操作符
 *
 *	这比 oper() 更严格，因为它不会返回一个需要
 *	强制转换输入数据类型的操作符（但接受二元兼容操作符）。
 *	否则，语义相同。
 */
Operator compatible_oper(ParseState *fc_pstate, List *fc_op, Oid fc_arg1, Oid fc_arg2,
				bool fc_noError, int fc_location)
{
	Operator	fc_optup;
	Form_pg_operator fc_opform;

	/* oper() 将查找最佳匹配 */
	fc_optup = oper(fc_pstate, fc_op, fc_arg1, fc_arg2, fc_noError, fc_location);
	if (fc_optup == (Operator) NULL)
		return (Operator) NULL; /* 必须是无错误情况 */

	/* 但这够好吗？ */
	fc_opform = (Form_pg_operator) GETSTRUCT(fc_optup);
	if (IsBinaryCoercible(fc_arg1, fc_opform->oprleft) &&
		IsBinaryCoercible(fc_arg2, fc_opform->oprright))
		return fc_optup;

	/* nope... */
	ReleaseSysCache(fc_optup);

	if (!fc_noError)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator requires run-time type coercion: %s",
						fc_op_signature_string(fc_op, 'b', fc_arg1, fc_arg2)),
				 parser_errposition(fc_pstate, fc_location)));

	return (Operator) NULL;
}

/* compatible_oper_opid() -- 获取二元运算符的 OID
 *
 * 这是一个便利例程，它仅从 compatible_oper() 的结果中提取运算符 OID。
 * 如果查找失败且 noError 为 true，则返回 InvalidOid。
 */
Oid compatible_oper_opid(List *fc_op, Oid fc_arg1, Oid fc_arg2, bool fc_noError)
{
	Operator	fc_optup;
	Oid			fc_result;

	fc_optup = compatible_oper(NULL, fc_op, fc_arg1, fc_arg2, fc_noError, -1);
	if (fc_optup != NULL)
	{
		fc_result = oprid(fc_optup);
		ReleaseSysCache(fc_optup);
		return fc_result;
	}
	return InvalidOid;
}


/* left_oper() -- 查找一元左运算符（前缀运算符）
 * 给定运算符名称和参数类型，返回 oper 结构。
 *
 * 重要提示：返回的运算符（如果有的话）仅承诺与输入数据类型具有类型兼容性。
 * 如果你需要精确或二进制兼容匹配，请不要使用这个。
 *
 * 如果没有找到匹配的运算符，如果 noError 为 true，则返回 NULL；
 * 如果为 false，则引发错误。pstate 和 location 仅用于报告错误位置；
 * 如果不可用，请传递 NULL/-1。
 *
 * 注意：成功时，返回的对象是系统缓存条目。调用者必须在使用完
 * 后释放这个条目。
 */
Operator left_oper(ParseState *fc_pstate, List *fc_op, Oid fc_arg, bool fc_noError, int fc_location)
{
	Oid			fc_operOid;
	OprCacheKey fc_key;
	bool		fc_key_ok;
	FuncDetailCode fc_fdresult = FUNCDETAIL_NOTFOUND;
	HeapTuple	fc_tup = NULL;

	/*
	 * 尝试在旁路缓存中找到映射。
	 */
	fc_key_ok = fc_make_oper_cache_key(fc_pstate, &fc_key, fc_op, InvalidOid, fc_arg, fc_location);

	if (fc_key_ok)
	{
		fc_operOid = fc_find_oper_cache_entry(&fc_key);
		if (OidIsValid(fc_operOid))
		{
			fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));
			if (HeapTupleIsValid(fc_tup))
				return (Operator) fc_tup;
		}
	}

	/*
	 * 首先尝试 "精确" 匹配。
	 */
	fc_operOid = OpernameGetOprid(fc_op, InvalidOid, fc_arg);
	if (!OidIsValid(fc_operOid))
	{
		/*
		 * 否则，搜索最合适的候选者。
		 */
		FuncCandidateList fc_clist;

		/* 获取给定名称的前缀运算符 */
		fc_clist = OpernameGetCandidates(fc_op, 'l', false);

		/* 找不到操作符？那就失败... */
		if (fc_clist != NULL)
		{
			/*
			 * 返回的列表以 (0, oprright) 的形式具有参数。将有用的数据移入 
			 * args[0]，以保持 oper_select_candidate 简单。
			 * XXX 我们在这里假设可以在列表上随意写入！
			 */
			FuncCandidateList fc_clisti;

			for (fc_clisti = fc_clist; fc_clisti != NULL; fc_clisti = fc_clisti->next)
			{
				fc_clisti->args[0] = fc_clisti->args[1];
			}

			/*
			 * 即使只有一个候选运算符，我们也必须运行 oper_select_candidate，
			 * 否则我们可能错误地返回一个与类型不兼容的运算符。
			 */
			fc_fdresult = fc_oper_select_candidate(1, &fc_arg, fc_clist, &fc_operOid);
		}
	}

	if (OidIsValid(fc_operOid))
		fc_tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(fc_operOid));

	if (HeapTupleIsValid(fc_tup))
	{
		if (fc_key_ok)
			fc_make_oper_cache_entry(&fc_key, fc_operOid);
	}
	else if (!fc_noError)
		fc_op_error(fc_pstate, fc_op, 'l', InvalidOid, fc_arg, fc_fdresult, fc_location);

	return (Operator) fc_tup;
}

/*
 * op_signature_string
 *		构建一个表示运算符名称的字符串，包括参数类型。
 *		结果类似于 "integer + integer"。
 *
 * 通常在构造运算符未找到错误消息时使用。
 */
static const char * fc_op_signature_string(List *fc_op, char fc_oprkind, Oid fc_arg1, Oid fc_arg2)
{
	StringInfoData fc_argbuf;

	initStringInfo(&fc_argbuf);

	if (fc_oprkind != 'l')
		appendStringInfo(&fc_argbuf, "%s ", format_type_be(fc_arg1));

	appendStringInfoString(&fc_argbuf, NameListToString(fc_op));

	appendStringInfo(&fc_argbuf, " %s", format_type_be(fc_arg2));

	return fc_argbuf.data;			/* 返回 palloc 的字符串缓冲区 */
}

/*
 * op_error - 实用程序例程，用于抱怨无法解决的运算符
 */
static void fc_op_error(ParseState *fc_pstate, List *fc_op, char fc_oprkind,
		 Oid fc_arg1, Oid fc_arg2,
		 FuncDetailCode fc_fdresult, int fc_location)
{
	if (fc_fdresult == FUNCDETAIL_MULTIPLE)
		ereport(ERROR,
				(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
				 errmsg("operator is not unique: %s",
						fc_op_signature_string(fc_op, fc_oprkind, fc_arg1, fc_arg2)),
				 errhint("Could not choose a best candidate operator. "
						 "You might need to add explicit type casts."),
				 parser_errposition(fc_pstate, fc_location)));
	else
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator does not exist: %s",
						fc_op_signature_string(fc_op, fc_oprkind, fc_arg1, fc_arg2)),
				 (!fc_arg1 || !fc_arg2) ?
				 errhint("No operator matches the given name and argument type. "
						 "You might need to add an explicit type cast.") :
				 errhint("No operator matches the given name and argument types. "
						 "You might need to add explicit type casts."),
				 parser_errposition(fc_pstate, fc_location)));
}

/*
 * make_op()
 *		运算符表达式构造。
 *
 * 转换运算符表达式，确保类型兼容性。
 * 这是进行某些类型转换的地方。
 *
 * last_srf 应该是 pstate->p_last_srf 的副本，就在我们开始转化运算符的
 * 参数之前；它用于嵌套 SRF 检测。如果调用者无论如何都会因集合返回
 * 表达式抛出错误，那么传递 pstate->p_last_srf 也没关系。
 */
Expr * make_op(ParseState *fc_pstate, List *fc_opname, Node *fc_ltree, Node *fc_rtree,
		Node *fc_last_srf, int fc_location)
{
	Oid			fc_ltypeId,
				fc_rtypeId;
	Operator	fc_tup;
	Form_pg_operator fc_opform;
	Oid			fc_actual_arg_types[2];
	Oid			fc_declared_arg_types[2];
	int			fc_nargs;
	List	   *fc_args;
	Oid			fc_rettype;
	OpExpr	   *fc_result;

	/* 检查这不是后缀运算符 */
	if (fc_rtree == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("postfix operators are not supported")));

	/* 选择运算符 */
	if (fc_ltree == NULL)
	{
		/* 前缀运算符 */
		fc_rtypeId = exprType(fc_rtree);
		fc_ltypeId = InvalidOid;
		fc_tup = left_oper(fc_pstate, fc_opname, fc_rtypeId, false, fc_location);
	}
	else
	{
		/* 否则，二元运算符 */
		fc_ltypeId = exprType(fc_ltree);
		fc_rtypeId = exprType(fc_rtree);
		fc_tup = oper(fc_pstate, fc_opname, fc_ltypeId, fc_rtypeId, false, fc_location);
	}

	fc_opform = (Form_pg_operator) GETSTRUCT(fc_tup);

	/* 检查这不是外壳 */
	if (!RegProcedureIsValid(fc_opform->oprcode))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator is only a shell: %s",
						fc_op_signature_string(fc_opname,
											fc_opform->oprkind,
											fc_opform->oprleft,
											fc_opform->oprright)),
				 parser_errposition(fc_pstate, fc_location)));

	/* 进行类型转换并构建表达式树 */
	if (fc_ltree == NULL)
	{
		/* 前缀运算符 */
		fc_args = list_make1(fc_rtree);
		fc_actual_arg_types[0] = fc_rtypeId;
		fc_declared_arg_types[0] = fc_opform->oprright;
		fc_nargs = 1;
	}
	else
	{
		/* 否则，二元运算符 */
		fc_args = list_make2(fc_ltree, fc_rtree);
		fc_actual_arg_types[0] = fc_ltypeId;
		fc_actual_arg_types[1] = fc_rtypeId;
		fc_declared_arg_types[0] = fc_opform->oprleft;
		fc_declared_arg_types[1] = fc_opform->oprright;
		fc_nargs = 2;
	}

	/* 强制保持多态参数和返回类型的一致性，可能调整返回类型或声明的参数类型（这些将被 make_fn_arguments 用作转换目标） */
	fc_rettype = enforce_generic_type_consistency(fc_actual_arg_types,
											   fc_declared_arg_types,
											   fc_nargs,
											   fc_opform->oprresult,
											   false);

	/* 执行必要的参数类型转换 */
	make_fn_arguments(fc_pstate, fc_args, fc_actual_arg_types, fc_declared_arg_types);

	/* 并构建表达式节点 */
	fc_result = makeNode(OpExpr);
	fc_result->opno = oprid(fc_tup);
	fc_result->opfuncid = fc_opform->oprcode;
	fc_result->opresulttype = fc_rettype;
	fc_result->opretset = get_func_retset(fc_opform->oprcode);
	/* opcollid 和 inputcollid 将由 parse_collate.c 设置 */
	fc_result->args = fc_args;
	fc_result->location = fc_location;

	/* 如果它返回一个集合，请检查这是否可以。 */
	if (fc_result->opretset)
	{
		check_srf_call_placement(fc_pstate, fc_last_srf, fc_location);
		/* ... 并在更高级别的错误检查中记住它 */
		fc_pstate->p_last_srf = (Node *) fc_result;
	}

	ReleaseSysCache(fc_tup);

	return (Expr *) fc_result;
}

/*
 * make_scalar_array_op()
 *		构建 "标量操作 ANY/ALL (array)" 构造的表达式树。
 */
Expr * make_scalar_array_op(ParseState *fc_pstate, List *fc_opname,
					 bool fc_useOr,
					 Node *fc_ltree, Node *fc_rtree,
					 int fc_location)
{
	Oid			fc_ltypeId,
				fc_rtypeId,
				fc_atypeId,
				fc_res_atypeId;
	Operator	fc_tup;
	Form_pg_operator fc_opform;
	Oid			fc_actual_arg_types[2];
	Oid			fc_declared_arg_types[2];
	List	   *fc_args;
	Oid			fc_rettype;
	ScalarArrayOpExpr *fc_result;

	fc_ltypeId = exprType(fc_ltree);
	fc_atypeId = exprType(fc_rtree);

	/*
	 * 运算符的右侧输入将是数组的元素类型。
	 * 但是，如果我们目前右侧只是一个未类型化的文字，保持不变，希望
	 * 我们可以解析运算符。
	 */
	if (fc_atypeId == UNKNOWNOID)
		fc_rtypeId = UNKNOWNOID;
	else
	{
		fc_rtypeId = get_base_element_type(fc_atypeId);
		if (!OidIsValid(fc_rtypeId))
			ereport(ERROR,
					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
					 errmsg("op ANY/ALL (array) requires array on right side"),
					 parser_errposition(fc_pstate, fc_location)));
	}

	/* 现在解析运算符 */
	fc_tup = oper(fc_pstate, fc_opname, fc_ltypeId, fc_rtypeId, false, fc_location);
	fc_opform = (Form_pg_operator) GETSTRUCT(fc_tup);

	/* 检查这不是外壳 */
	if (!RegProcedureIsValid(fc_opform->oprcode))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_FUNCTION),
				 errmsg("operator is only a shell: %s",
						fc_op_signature_string(fc_opname,
											fc_opform->oprkind,
											fc_opform->oprleft,
											fc_opform->oprright)),
				 parser_errposition(fc_pstate, fc_location)));

	fc_args = list_make2(fc_ltree, fc_rtree);
	fc_actual_arg_types[0] = fc_ltypeId;
	fc_actual_arg_types[1] = fc_rtypeId;
	fc_declared_arg_types[0] = fc_opform->oprleft;
	fc_declared_arg_types[1] = fc_opform->oprright;

	/* 强制保持多态参数和返回类型的一致性，可能调整返回类型或声明的参数类型（这些将被 make_fn_arguments 用作转换目标） */
	fc_rettype = enforce_generic_type_consistency(fc_actual_arg_types,
											   fc_declared_arg_types,
											   2,
											   fc_opform->oprresult,
											   false);

	/*
	 * 检查运算符结果是否为布尔型
	 */
	if (fc_rettype != BOOLOID)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("op ANY/ALL (array) requires operator to yield boolean"),
				 parser_errposition(fc_pstate, fc_location)));
	if (get_func_retset(fc_opform->oprcode))
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("op ANY/ALL (array) requires operator not to return a set"),
				 parser_errposition(fc_pstate, fc_location)));

	/*
	 * 现在切换回右侧的数组类型，安排应用任何必要的转换。
	 * 这里要小心多态运算符； enforce_generic_type_consistency 可能
	 * 已经用真实类型替换了多态类型。
	 */
	if (IsPolymorphicType(fc_declared_arg_types[1]))
	{
		/* 假设实际数组类型是可以的 */
		fc_res_atypeId = fc_atypeId;
	}
	else
	{
		fc_res_atypeId = get_array_type(fc_declared_arg_types[1]);
		if (!OidIsValid(fc_res_atypeId))
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("could not find array type for data type %s",
							format_type_be(fc_declared_arg_types[1])),
					 parser_errposition(fc_pstate, fc_location)));
	}
	fc_actual_arg_types[1] = fc_atypeId;
	fc_declared_arg_types[1] = fc_res_atypeId;

	/* 执行必要的参数类型转换 */
	make_fn_arguments(fc_pstate, fc_args, fc_actual_arg_types, fc_declared_arg_types);

	/* 并构建表达式节点 */
	fc_result = makeNode(ScalarArrayOpExpr);
	fc_result->opno = oprid(fc_tup);
	fc_result->opfuncid = fc_opform->oprcode;
	fc_result->hashfuncid = InvalidOid;
	fc_result->negfuncid = InvalidOid;
	fc_result->useOr = fc_useOr;
	/* inputcollid 将由 parse_collate.c 设置 */
	fc_result->args = fc_args;
	fc_result->location = fc_location;

	ReleaseSysCache(fc_tup);

	return (Expr *) fc_result;
}


/*
 * 旁路缓存以加速运算符查找。可能这应该在 utils/cache/ 下的单独模块中？
 *
 * 这里的想法是，运算符名称和给定参数类型的映射对于给定的搜索路径
 * （或单一指定的模式 OID）是常量，只要 pg_operator 和 pg_cast 的内容
 * 不变。而且这个映射计算起来相当昂贵，尤其是对于模糊运算符；
 * 这主要是因为我们有很多流行运算符名称的实例，例如 "="，
 * 我们必须检查每一个以查看哪一个是最佳匹配。因此，一旦我们识别出
 * 正确的映射，我们将其保存在缓存中，仅在 pg_operator 或 pg_cast 更改时
 * 刷新。(pg_cast 必须被考虑，因为隐式转换集合的变化会影响
 * 任何给定输入数据类型的适用运算符集合。)
 *
 * XXX 原则上，ALTER TABLE ... INHERIT 也可能影响映射，
 * 但我们忽略这一点，因为没有方便的方法来找到它，而这似乎是一个相当
 * 牵强的边缘案例。
 *
 * 注意：到某个时候，为函数查找做一个类似的缓存可能是值得的。
 * 然而，潜在的收益要小得多，因为 (a) 函数名称通常没有像运算符名称那样被重载，
 * 并且 (b) 我们必须在 pg_proc 更新时刷新，后者可能比 pg_operator 更新
 * 更常见。
 */

/* 运算符缓存哈希表 */
static HTAB *OprCacheHash = NULL;



/*
 * make_oper_cache_key
 *		根据操作符名称和参数类型填充查找键结构。
 *
 * 如果成功则返回true，如果search_path溢出则返回false
 * （因此无法缓存）。
 *
 * pstate/location仅用于报告错误位置；如果不可用，则传递NULL/-1。
 */
static bool fc_make_oper_cache_key(ParseState *fc_pstate, OprCacheKey *fc_key, List *fc_opname,
					Oid fc_ltypeId, Oid fc_rtypeId, int fc_location)
{
	char	   *fc_schemaname;
	char	   *fc_opername;

	/* 解构名称列表 */
	DeconstructQualifiedName(fc_opname, &fc_schemaname, &fc_opername);

	/* 确保零填充以进行稳定哈希 */
	MemSet(fc_key, 0, sizeof(OprCacheKey));

	/* 将操作符名称和输入类型保存到键中 */
	strlcpy(fc_key->oprname, fc_opername, NAMEDATALEN);
	fc_key->left_arg = fc_ltypeId;
	fc_key->right_arg = fc_rtypeId;

	if (fc_schemaname)
	{
		ParseCallbackState fc_pcbstate;

		/* 仅在给定的确切模式中搜索 */
		setup_parser_errposition_callback(&fc_pcbstate, fc_pstate, fc_location);
		fc_key->search_path[0] = LookupExplicitNamespace(fc_schemaname, false);
		cancel_parser_errposition_callback(&fc_pcbstate);
	}
	else
	{
		/* 获取活动搜索路径 */
		if (fetch_search_path_array(fc_key->search_path,
									MAX_CACHED_PATH_LEN) > MAX_CACHED_PATH_LEN)
			return false;		/* 糟糕，没适配 */
	}

	return true;
}

/*
 * find_oper_cache_entry
 *
 * 查找匹配给定键的缓存条目。如果找到，返回包含的操作符OID，否则返回InvalidOid。
 */
static Oid fc_find_oper_cache_entry(OprCacheKey *fc_key)
{
	OprCacheEntry *fc_oprentry;

	if (OprCacheHash == NULL)
	{
		/* 第一次遍历：初始化哈希表 */
		HASHCTL		fc_ctl;

		fc_ctl.keysize = sizeof(OprCacheKey);
		fc_ctl.entrysize = sizeof(OprCacheEntry);
		OprCacheHash = hash_create("Operator lookup cache", 256,
								   &fc_ctl, HASH_ELEM | HASH_BLOBS);

		/* 安排在pg_operator和pg_cast更改时刷新缓存 */
		CacheRegisterSyscacheCallback(OPERNAMENSP,
									  fc_InvalidateOprCacheCallBack,
									  (Datum) 0);
		CacheRegisterSyscacheCallback(CASTSOURCETARGET,
									  fc_InvalidateOprCacheCallBack,
									  (Datum) 0);
	}

	/* 查找现有条目 */
	fc_oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
											 (void *) fc_key,
											 HASH_FIND, NULL);
	if (fc_oprentry == NULL)
		return InvalidOid;

	return fc_oprentry->opr_oid;
}

/*
 * make_oper_cache_entry
 *
 * 为给定键插入一个缓存条目。
 */
static void fc_make_oper_cache_entry(OprCacheKey *fc_key, Oid fc_opr_oid)
{
	OprCacheEntry *fc_oprentry;

	Assert(OprCacheHash != NULL);

	fc_oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
											 (void *) fc_key,
											 HASH_ENTER, NULL);
	fc_oprentry->opr_oid = fc_opr_oid;
}

/*
 * pg_operator和pg_cast无效事件的回调
 */
static void fc_InvalidateOprCacheCallBack(Datum fc_arg, int fc_cacheid, uint32 fc_hashvalue)
{
	HASH_SEQ_STATUS fc_status;
	OprCacheEntry *fc_hentry;

	Assert(OprCacheHash != NULL);

	/* 目前我们只是刷新所有条目；很难更聪明... */
	hash_seq_init(&fc_status, OprCacheHash);

	while ((fc_hentry = (OprCacheEntry *) hash_seq_search(&fc_status)) != NULL)
	{
		if (hash_search(OprCacheHash,
						(void *) &fc_hentry->key,
						HASH_REMOVE, NULL) == NULL)
			elog(ERROR, "hash table corrupted");
	}
}
